﻿/*******************************************************************************************
 * Copyright (c) 2006-2009 Alex Robson, Rob Simmons, Craig Israel, and Rachel Twyford
 *
 * Use of this software in any form requires that you have both 
 * read and agreed to the following terms:
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the "Software"), 
 * to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, 
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions: 
 *
 * Redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright notice, 
 * this list of conditions and the following disclaimer in the 
 * documentation and/or other materials provided with the distribution.
 * Neither the name of Nvigorate nor the names of its contributors may 
 * be used to endorse or promote products derived from this software without 
 * specific prior written permission. 
 *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
 * DISCLAIMED. IN NO EVENT SHALL ANY CONTRIBUTOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * ****************************************************************************************/

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Schema;
using System.Xml.Serialization;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using Nvigorate.Common;
using Nvigorate.Relational.Mapping.Index;
using Nvigorate.Relational.Mapping.Interface;
using Nvigorate.Relational.Record;
using Nvigorate.Relational.State;

namespace Nvigorate.Test.Relational.Binding
{
    [TestClass]
    public class XmlBindingMapLoaderTests : BindingTestBase
    {
        #region GetMap Tests

        [TestMethod]
        public void GetMap_PathContainsXml_DeserializesXmlIntoMap()
        {
            // Arrange
            var path = "c:\\temp\\Employee.binding";
            var streamReaderFactory = new Mock<IStreamReaderFactory>();
            streamReaderFactory.Expect(f => f.Create(path)).Returns(
                CreateStreamReader("<map target=\"classA\" />"));

            // Act
            var loader = new XmlBindingMapLoader(path, typeof (SerializableType),
                                                 streamReaderFactory.Object);
            var map = loader.GetMap();

            // Assert
            Assert.AreEqual("classA", map.TargetName);
            Assert.AreEqual("", map.RepositoryName);
        }

        [TestMethod]
        public void GetMap_MapSetProvidedInConstructorAndDataSourceInXml_UsesXmlValueForDataSource()
        {
            // Arrange
            var path = "c:\\temp\\Employee.binding";
            var streamReaderFactory = new Mock<IStreamReaderFactory>();
            streamReaderFactory.Expect(f => f.Create(path)).Returns(
                CreateStreamReader("<map target=\"classA\" repositoryName=\"MapSetA\" />"));

            // Act
            var loader = new XmlBindingMapLoader(path, typeof(SerializableType),
                                                 streamReaderFactory.Object);
            var map = loader.GetMap();

            // Assert
            Assert.AreEqual("MapSetA", map.RepositoryName);            
        }

        [TestMethod]
        public void GetMap_MapSetProvidedInConstructorAndNoDataSourceInXml_UsesMapSetForDataSource()
        {
            // Arrange
            var path = "c:\\temp\\Employee.binding";
            var streamReaderFactory = new Mock<IStreamReaderFactory>();
            streamReaderFactory.Expect(f => f.Create(path)).Returns(
                CreateStreamReader("<map target=\"classA\" repositoryName=\"MapSetA\" />"));

            // Act
            var loader = new XmlBindingMapLoader(path, typeof(SerializableType),
                                                 streamReaderFactory.Object);
            var map = loader.GetMap();

            // Assert
            Assert.AreEqual("MapSetA", map.RepositoryName);            
        }

        #endregion
        
        #region Private Methods

        private StreamReader CreateStreamReader(string streamContents)
        {
            var stream = new MemoryStream();
            var writer = new XmlTextWriter(stream, Encoding.UTF8);
            writer.WriteRaw(streamContents);
            writer.Flush();
            stream.Position = 0;
            return new StreamReader(stream, Encoding.UTF8);
        }
        #endregion
        


        #region Nested Types

        public class SerializableType : IXmlSerializable, IBindingMap
        {
            public string TargetName { get; set; }
            public XmlSchema GetSchema()
            {
                return null;
            }

            public void ReadXml(XmlReader reader)
            {
                if (reader.Read())
                {
                    var temp = reader.ReadOuterXml();
                    var element = XElement.Parse(temp);
                    TargetName = element.Attributes("target").First().Value;
                    RepositoryName = element.Attributes("repositoryName").Count() > 0 ? element.Attributes("repositoryName").First().Value : "";
                }
            }

            public void WriteXml(XmlWriter writer)
            {
            }

            #region IBindingMap members

            /// <summary>
            /// Gets or sets a value indicating whether the delete operation is available 
            /// for the mapped type.
            /// </summary>
            /// <value>
            /// 	<c>true</c> if this instance can delete; otherwise, <c>false</c>.
            /// </value>
            public bool CanDelete { get; set; }

            /// <summary>
            /// Gets or sets a value indicating whether insert operation is available 
            /// for the mapped type.
            /// </summary>
            /// <value>
            /// 	<c>true</c> if this instance can insert; otherwise, <c>false</c>.
            /// </value>
            public bool CanInsert { get; set; }

            /// <summary>
            /// Gets or sets a value indicating whether the select operation is available 
            /// for the mapped type.
            /// </summary>
            /// <value>
            /// 	<c>true</c> if this instance can select; otherwise, <c>false</c>.
            /// </value>
            public bool CanSelect { get; set; }

            /// <summary>
            /// Gets or sets a value indicating whether the update operation is available 
            /// for the mapped type.
            /// </summary>
            /// <value>
            /// 	<c>true</c> if this instance can update; otherwise, <c>false</c>.
            /// </value>
            public bool CanUpdate { get; set; }

            public IDiscriminate Discriminator { get; set; }

            public string RepositoryName { get; set; }

            public Type TargetType
            {
                get { throw new NotImplementedException(); }
                set { throw new NotImplementedException(); }
            }

            /// <summary>
            /// A list of relationships between this mapped type and others
            /// </summary>
            public List<ITargetRelationship> Relationships
            {
                get { throw new System.NotImplementedException(); }
                set { throw new System.NotImplementedException(); }
            }

            public Dictionary<string, ISourceMap> Sources
            {
                get { throw new NotImplementedException(); }
                set { throw new NotImplementedException(); }
            }

            public void AddAssignment(IAssignment assignment)
            {
                throw new NotImplementedException();
            }

            public void AddJoin(IJoin join)
            {
                throw new NotImplementedException();
            }

            public void AddSource(ISourceMap map)
            {
                throw new NotImplementedException();
            }

            public IEnumerable<IAssignment> GetAssignments()
            {
                throw new NotImplementedException();
            }

            public IEnumerable<IAssignment> GetAssignmentsByPredicate(Func<IAssignment, bool> predicate)
            {
                throw new NotImplementedException();
            }

            public IAssignment GetAssignmentBySourceField(string source, string field)
            {
                throw new NotImplementedException();
            }

            public IAssignment GetAssignmentByTargetField(string source, string field)
            {
                throw new NotImplementedException();
            }

            public IEnumerable<IAssignment> GetAssignmentsByTargetField(string field)
            {
                throw new NotImplementedException();
            }

            /// <summary>
            /// Returns child source maps
            /// </summary>
            /// <returns>Returns all maps for the type except the root</returns>
            public IEnumerable<ISourceMap> GetChildSources()
            {
                throw new System.NotImplementedException();
            }

            /// <summary>
            /// Returns child source maps
            /// </summary>
            /// <returns>Returns all child maps of the parent map for the type</returns>
            public IEnumerable<ISourceMap> GetChildSources(string parentSourceName)
            {
                throw new System.NotImplementedException();
            }

            /// <summary>
            /// Find a relationship to the related type for this mapped type
            /// </summary>
            /// <param name="relatedType">The subject or relative type</param>
            /// <returns>The relationship which ties this mapped type to the relatedType</returns>
            public T GetRelationshipForType<T>(Type relatedType) where T : ITargetRelationship
            {
                throw new System.NotImplementedException();
            }

            public string GetRootName()
            {
                throw new NotImplementedException();
            }

            public ISourceMap GetRoot()
            {
                throw new NotImplementedException();
            }

            public IEnumerable<IAssignment> GetSuppliedAssignments(IRecord sourceRecord)
            {
                throw new NotImplementedException();
            }

            public void MarkPersistedSources(IPersistenceState state)
            {
                throw new NotImplementedException();
            }

            public void MarkTargetKeys(IPersistenceState state)
            {
                throw new NotImplementedException();
            }

            /// <summary>
            /// Gets the source maps in create order
            /// </summary>
            /// <returns>The source maps in create order</returns>
            public IEnumerable<ISourceMap> GetSourceMapsInCreateOrder(bool reverse)
            {
                throw new System.NotImplementedException();
            }

            /// <summary>
            /// The types which this map inherits the map from
            /// </summary>
            public IList<Type> Inherits
            {
                get { throw new NotImplementedException(); }
                set { throw new NotImplementedException(); }
            }

            /// <summary>
            /// Provides a list of types which inherit from this map
            /// </summary>
            public IList<Type> InheritedBy
            {
                get { throw new NotImplementedException(); }
                set { throw new NotImplementedException(); }
            }

            /// <summary>
            /// Gets the child types which inherit their map from this one
            /// </summary>
            /// <returns>The child types which inherit from this</returns>
            public IEnumerable<Type> GetChildTypes()
            {
                throw new NotImplementedException();
            }

            /// <summary>
            /// Merges a parent map into this one
            /// </summary>
            /// <param name="parentMap"></param>
            public void Merge(IBindingMap parentMap)
            {
                throw new NotImplementedException();
            }

            #endregion
        }

        #endregion
    }
}